﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Consul;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Provider.Consul;
using Ocelot.Provider.Polly;
using OcelotApiGw.Infrastructure;
using Serilog;
using Swashbuckle.AspNetCore.Swagger;

namespace OcelotApiGw
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            var identityUrl = Configuration.GetValue<string>("IdentityUrl");
            var authenticationProviderKey = "IdentityApiKey";

            services.AddHealthChecks()
                .AddCheck("self", () => HealthCheckResult.Healthy())
                .AddUrlGroup(new Uri(Configuration["SystemManageUrlHC"]), name: "systemmanageapi-check", tags: new string[] { "systemmanageapi" })
                .AddUrlGroup(new Uri(Configuration["WorkOrderManageUrlHC"]), name: "workordermanageapi-check", tags: new string[] { "workordermanageapi" })
                .AddUrlGroup(new Uri(Configuration["IdentityUrlHC"]), name: "identityapi-check", tags: new string[] { "identityapi" })
                .AddUrlGroup(new Uri(Configuration["WeChatServiceUrlHC"]), name: "wechatserviceapi-check", tags: new string[] { "wechatserviceapi" });
            services.AddCors(options =>
            {
                options.AddPolicy("CorsPolicy",
                    builder => builder
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .SetIsOriginAllowed((host) => true)
                    .AllowCredentials());
            });
            services.AddAuthentication()
                .AddJwtBearer(authenticationProviderKey, x =>
                {
                    x.Authority = identityUrl;
                    x.RequireHttpsMetadata = false;
                    x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
                    {
                        ValidAudiences = new[] {"wxservice"}
                    };
                    x.Events = new Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents()
                    {
                        OnAuthenticationFailed = async ctx =>
                        {
                            Log.Error($"权限校验失败:{ctx.Exception.Message}");
                            await Task.CompletedTask;
                        },
                        OnTokenValidated = async ctx =>
                        {
                            Log.Information("权限校验完成");
                            await Task.CompletedTask;
                        },

                        OnMessageReceived = async ctx =>
                        {
                            Log.Information("接收权限校验消息");
                            await Task.CompletedTask;
                        }
                    };
                });
            services.AddOcelot(Configuration).AddConsul().AddPolly();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env,Microsoft.Extensions.Hosting.IHostApplicationLifetime applicationLifetime)
        {
            var pathBase = Configuration["PATH_BASE"];

            if (!string.IsNullOrEmpty(pathBase))
            {
                app.UsePathBase(pathBase);
            }

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHealthChecks("/hc", new HealthCheckOptions()
            {
                Predicate = _ => true,
                ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
            });

            app.UseHealthChecks("/liveness", new HealthCheckOptions
            {
                Predicate = r => r.Name.Contains("self")
            });
            //app.UseMvc();
            app.UseCors("CorsPolicy");

            SetConsul(app, applicationLifetime);

            app.UseWebSockets();
            app.UseOcelot().Wait();
        }

        #region Consul 客户端配置
        private void SetConsul(IApplicationBuilder app, Microsoft.Extensions.Hosting.IHostApplicationLifetime applicationLifetime)
        {
            string consulServicesID = Configuration["ServicesName"] + Guid.NewGuid();
            using (var client = new ConsulClient(consulClient =>
            {
                consulClient.Address = new Uri(Configuration["ConsulUrl"]);
                consulClient.Datacenter = "dc1";
            }))
            {
                //注册服务到 Consul
                client.Agent.ServiceRegister(new AgentServiceRegistration()
                {
                    ID = consulServicesID,//服务编号，不能重复，用 Guid 最简单
                    Name = Configuration["ServicesName"],//服务的名字
                    Address = Configuration["SerivceIP"],//当前服务的ip地址(可以被其他应用访问的地址，本地测试可以用127.0.0.1，机房环境中一定要写自己的内网 ip 地址)
                    Port = Convert.ToInt32(Configuration["SerivcePort"]),//当前服务的端口
                    Tags = new[] { $"urlprefix-/{Configuration["ServicesName"]}" },//添加 urlprefix-/servicename 格式的 tag 标签，以便 Fabio 识别
                    Check = new AgentServiceCheck
                    {
                        DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务停止多久后反注册
                        Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔，或者称为心跳间隔
                        HTTP = $"{Configuration["HealthUrl"]}",//健康检查地址
                        Timeout = TimeSpan.FromSeconds(5)
                    }
                }).Wait();//Consult 客户端的所有方法几乎都是异步方法，但是都没按照规范加上Async 后缀，所以容易误导。记得调用后要 Wait()或者 await
            }
            //程序正常退出的时候从 Consul 注销服务
            //要通过方法参数注入 IApplicationLifetime
            applicationLifetime.ApplicationStopped.Register(() =>
            {
                using (var client = new ConsulClient(consulClient =>
                {
                    consulClient.Address = new Uri(Configuration["ConsulUrl"]);
                    consulClient.Datacenter = "dc1";
                }))
                {
                    //ServiceDeregister异步方法增加 Wait 等待完成
                    client.Agent.ServiceDeregister(consulServicesID).Wait();
                }
            });
        }
        #endregion

    }
}
